+use std::collections::HashMap;
use std::io::fs::{mod, PathExtensions};
use core::{MultiShell, PackageSet};
let pkgs = PackageSet::new([]);
let cx = try!(Context::new("compile", &resolve, &srcs, &pkgs, &mut cfg,
Layout::at(root.get_absolute_target_dir()),
- None, &pkg));
+ None, &pkg, HashMap::new()));
// And finally, clean everything out!
for target in pkg.get_targets().iter() {
use core::registry::PackageRegistry;
use core::{MultiShell, Source, SourceId, PackageSet, Package, Target, PackageId};
use core::resolver;
-use ops;
+use ops::{mod, BuildOutput};
use sources::{PathSource};
use util::config::{Config, ConfigValue};
use util::{CargoResult, Wrap, config, internal, human, ChainError, profile};
let ret = {
let _p = profile::start("compiling");
- try!(scrape_target_config(&config, &user_configs));
+ let lib_overrides = try!(scrape_target_config(&config, &user_configs));
try!(ops::compile_targets(env.as_slice(), targets.as_slice(), to_build,
&PackageSet::new(packages.as_slice()),
&resolve_with_overrides, &sources,
- &config))
+ &config, lib_overrides))
};
return Ok(ret);
fn scrape_target_config(config: &Config,
configs: &HashMap<String, config::ConfigValue>)
- -> CargoResult<()> {
+ -> CargoResult<HashMap<String, BuildOutput>> {
let target = match configs.find_equiv("target") {
- None => return Ok(()),
+ None => return Ok(HashMap::new()),
Some(target) => try!(target.table().chain_error(|| {
internal("invalid configuration for the key `target`")
})),
};
- let target = match config.target() {
- None => target,
- Some(triple) => match target.find_equiv(triple) {
- None => return Ok(()),
- Some(target) => try!(target.table().chain_error(|| {
- internal(format!("invalid configuration for the key \
- `target.{}`", triple))
- })),
- },
+ let triple = config.target().unwrap_or(config.rustc_host()).to_string();
+ let target = match target.find(&triple) {
+ None => return Ok(HashMap::new()),
+ Some(target) => try!(target.table().chain_error(|| {
+ internal(format!("invalid configuration for the key \
+ `target.{}`", triple))
+ })),
};
- match target.find_equiv("ar") {
- None => {}
- Some(ar) => {
- config.set_ar(try!(ar.string().chain_error(|| {
- internal("invalid configuration for key `ar`")
- })).ref0().to_string());
- }
- }
-
- match target.find_equiv("linker") {
- None => {}
- Some(linker) => {
- config.set_linker(try!(linker.string().chain_error(|| {
- internal("invalid configuration for key `ar`")
- })).ref0().to_string());
+ let mut ret = HashMap::new();
+ for (k, v) in target.iter() {
+ match k.as_slice() {
+ "ar" | "linker" => {
+ let v = try!(v.string().chain_error(|| {
+ internal(format!("invalid configuration for key `{}`", k))
+ })).ref0().to_string();
+ if k.as_slice() == "linker" {
+ config.set_linker(v);
+ } else {
+ config.set_ar(v);
+ }
+ }
+ lib_name => {
+ let table = try!(v.table().chain_error(|| {
+ internal(format!("invalid configuration for the key \
+ `target.{}.{}`", triple, lib_name))
+ }));
+ let mut output = BuildOutput {
+ library_paths: Vec::new(),
+ library_links: Vec::new(),
+ metadata: Vec::new(),
+ };
+ for (k, v) in table.iter() {
+ let v = try!(v.string().chain_error(|| {
+ internal(format!("invalid configuration for the key \
+ `target.{}.{}.{}`", triple, lib_name,
+ k))
+ })).val0();
+ if k.as_slice() == "rustc-flags" {
+ let whence = format!("in `target.{}.{}.rustc-flags`",
+ triple, lib_name);
+ let whence = whence.as_slice();
+ let (paths, links) = try!(
+ BuildOutput::parse_rustc_flags(v.as_slice(), whence)
+ );
+ output.library_paths.extend(paths.into_iter());
+ output.library_links.extend(links.into_iter());
+ } else {
+ output.metadata.push((k.to_string(), v.to_string()));
+ }
+ }
+ ret.insert(lib_name.to_string(), output);
+ }
}
}
- Ok(())
+ Ok(ret)
}
use std::collections::HashSet;
use std::collections::hash_map::{HashMap, Occupied, Vacant};
use std::str;
+use std::sync::{Arc, Mutex};
use core::{SourceMap, Package, PackageId, PackageSet, Resolve, Target};
use util::{mod, CargoResult, ChainError, internal, Config, profile};
use util::human;
-use super::{Kind, KindHost, KindTarget, Compilation};
+use super::{Kind, KindHost, KindTarget, Compilation, BuildOutput};
use super::layout::{Layout, LayoutProxy};
#[deriving(Show)]
pub resolve: &'a Resolve,
pub sources: &'a SourceMap<'b>,
pub compilation: Compilation,
+ pub native_libs: Arc<Mutex<HashMap<String, BuildOutput>>>,
env: &'a str,
host: Layout,
pub fn new(env: &'a str, resolve: &'a Resolve, sources: &'a SourceMap<'b>,
deps: &'a PackageSet, config: &'b Config<'b>,
host: Layout, target: Option<Layout>,
- root_pkg: &Package)
+ root_pkg: &Package,
+ native_libs: HashMap<String, BuildOutput>)
-> CargoResult<Context<'a, 'b>> {
let (target_dylib, target_exe) =
try!(Context::filename_parts(config.target()));
host_dylib: host_dylib,
requirements: HashMap::new(),
compilation: Compilation::new(root_pkg),
+ native_libs: Arc::new(Mutex::new(native_libs)),
})
}
None => return vec!(),
Some(deps) => deps,
};
- deps.map(|pkg_id| self.get_package(pkg_id))
- .filter_map(|pkg| {
+ deps.map(|pkg_id| self.get_package(pkg_id)).filter_map(|pkg| {
pkg.get_targets().iter().find(|&t| self.is_relevant_target(t))
.map(|t| (pkg, t))
- })
- .collect()
+ }).collect()
}
/// Gets a package for the given package id.
-use std::io::{fs, BufferedReader, BufReader, USER_RWX};
-use std::io::fs::{File, PathExtensions};
+use std::io::{fs, BufReader, USER_RWX};
+use std::io::fs::PathExtensions;
use core::{Package, Target};
use util::{CargoResult, CargoError, human};
use super::job::Work;
use super::{process, KindHost, Context};
+/// Contains the parsed output of a custom build script.
+#[deriving(Clone)]
+pub struct BuildOutput {
+ /// Paths to pass to rustc with the `-L` flag
+ pub library_paths: Vec<Path>,
+ /// Names and link kinds of libraries, suitable for the `-l` flag
+ pub library_links: Vec<String>,
+ /// Metadata to pass to the immediate dependencies
+ pub metadata: Vec<(String, String)>,
+}
+
/// Prepares a `Work` that executes the target as a custom build script.
pub fn prepare_execute_custom_build(pkg: &Package, target: &Target,
- cx: &mut Context)
- -> CargoResult<Work> {
+ cx: &mut Context)
+ -> CargoResult<Work> {
let layout = cx.layout(pkg, KindHost);
let script_output = layout.build(pkg);
let build_output = layout.build_out(pkg);
match cx.resolve.features(pkg.get_package_id()) {
Some(features) => {
for feat in features.iter() {
- let feat = feat.as_slice().chars()
- .map(|c| c.to_uppercase())
- .map(|c| if c == '-' {'_'} else {c})
- .collect::<String>();
- p = p.env(format!("CARGO_FEATURE_{}", feat).as_slice(), Some("1"));
+ p = p.env(format!("CARGO_FEATURE_{}",
+ super::envify(feat.as_slice())).as_slice(),
+ Some("1"));
}
}
None => {}
}
- // building the list of all possible `build/$pkg/output` files
- // whether they exist or not will be checked during the work
- let command_output_files = {
- let layout = cx.layout(pkg, KindHost);
- cx.dep_targets(pkg).iter().map(|&(pkg, _)| {
- layout.build(pkg).join("output")
- }).collect::<Vec<_>>()
+ // Gather the set of native dependencies that this package has
+ let lib_deps = {
+ cx.dep_targets(pkg).iter().filter_map(|&(pkg, _)| {
+ pkg.get_manifest().get_links()
+ }).map(|s| s.to_string()).collect::<Vec<_>>()
};
+ let native_libs = cx.native_libs.clone();
+
// Building command
let pkg = pkg.to_string();
let work = proc(desc_tx: Sender<String>) {
- desc_tx.send_opt(build_output.display().to_string()).ok();
if !build_output.exists() {
- try!(fs::mkdir(&build_output, USER_RWX)
- .chain_error(|| {
- internal("failed to create build output directory for build command")
- }))
+ try!(fs::mkdir(&build_output, USER_RWX).chain_error(|| {
+ internal("failed to create build output directory for \
+ build command")
+ }))
}
- // loading each possible custom build output file in order to get their metadata
- let _metadata = {
- let mut metadata = Vec::new();
-
- for flags_file in command_output_files.into_iter() {
- match File::open(&flags_file) {
- Ok(flags) => {
- let flags = try!(CustomBuildCommandOutput::parse(
- BufferedReader::new(flags), pkg.as_slice()));
- metadata.extend(flags.metadata.into_iter());
- },
- Err(_) => () // the file doesn't exist, probably means that this pkg
- // doesn't have a build command
+ // loading each possible custom build output file in order to get their
+ // metadata
+ let mut p = p;
+ {
+ let native_libs = native_libs.lock();
+ for dep in lib_deps.iter() {
+ for &(ref key, ref value) in (*native_libs)[*dep].metadata.iter() {
+ p = p.env(format!("DEP_{}_{}",
+ super::envify(dep.as_slice()),
+ super::envify(key.as_slice())).as_slice(),
+ Some(value.as_slice()));
}
}
+ }
- metadata
- };
-
- // TODO: ENABLE THIS CODE WHEN `links` IS ADDED
- /*let mut p = p;
- for (key, value) in metadata.into_iter() {
- p = p.env(format!("DEP_{}_{}", PUT LINKS VALUES HERE, value), value);
- }*/
-
+ desc_tx.send_opt(p.to_string()).ok();
let output = try!(p.exec_with_output().map_err(|mut e| {
e.msg = format!("Failed to run custom build command for `{}`\n{}",
pkg, e.msg);
}));
// parsing the output of the custom build script to check that it's correct
- try!(CustomBuildCommandOutput::parse(BufReader::new(output.output.as_slice()),
+ try!(BuildOutput::parse(BufReader::new(output.output.as_slice()),
pkg.as_slice()));
// writing the output to the right directory
Ok(work)
}
-/// Contains the parsed output of a custom build script.
-pub struct CustomBuildCommandOutput {
- /// Paths to pass to rustc with the `-L` flag
- pub library_paths: Vec<Path>,
- /// Names and link kinds of libraries, suitable for the `-l` flag
- pub library_links: Vec<String>,
- /// Metadata to pass to the immediate dependencies
- pub metadata: Vec<(String, String)>,
-}
-
-impl CustomBuildCommandOutput {
+impl BuildOutput {
// Parses the output of a script.
// The `pkg_name` is used for error messages.
- pub fn parse<B: Buffer>(mut input: B, pkg_name: &str) -> CargoResult<CustomBuildCommandOutput> {
+ pub fn parse<B: Buffer>(mut input: B, pkg_name: &str) -> CargoResult<BuildOutput> {
let mut library_paths = Vec::new();
let mut library_links = Vec::new();
let mut metadata = Vec::new();
+ let whence = format!("build script of `{}`", pkg_name);
for line in input.lines() {
// unwrapping the IoResult
let (key, value) = match (key, value) {
(Some(a), Some(b)) => (a, b),
// line started with `cargo:` but didn't match `key=value`
- _ => return Err(human(format!("Wrong output for the custom\
- build script of `{}`:\n`{}`", pkg_name, line)))
+ _ => return Err(human(format!("Wrong output in {}: `{}`",
+ whence, line)))
};
if key == "rustc-flags" {
- // TODO: some arguments (like paths) may contain spaces
- let mut flags_iter = value.words();
- loop {
- let flag = match flags_iter.next() {
- Some(f) => f,
- None => break
- };
- if flag != "-l" && flag != "-L" {
- return Err(human(format!("Only `-l` and `-L` flags are allowed \
- in build script of `{}`:\n`{}`",
- pkg_name, value)))
- }
- let value = match flags_iter.next() {
- Some(v) => v,
- None => return Err(human(format!("Flag in rustc-flags has no value\
- in build script of `{}`:\n`{}`",
- pkg_name, value)))
- };
- match flag {
- "-l" => library_links.push(value.to_string()),
- "-L" => library_paths.push(Path::new(value)),
-
- // was already checked above
- _ => return Err(human("only -l and -L flags are allowed"))
- };
- }
+ let whence = whence.as_slice();
+ let (libs, links) = try!(
+ BuildOutput::parse_rustc_flags(value, whence)
+ );
+ library_links.extend(links.into_iter());
+ library_paths.extend(libs.into_iter());
} else {
metadata.push((key.to_string(), value.to_string()))
}
}
- Ok(CustomBuildCommandOutput {
+ Ok(BuildOutput {
library_paths: library_paths,
library_links: library_links,
metadata: metadata,
})
}
+
+ pub fn parse_rustc_flags(value: &str, whence: &str)
+ -> CargoResult<(Vec<Path>, Vec<String>)> {
+ // TODO: some arguments (like paths) may contain spaces
+ let value = value.trim();
+ let mut flags_iter = value.words();
+ let (mut library_links, mut library_paths) = (Vec::new(), Vec::new());
+ loop {
+ let flag = match flags_iter.next() {
+ Some(f) => f,
+ None => break
+ };
+ if flag != "-l" && flag != "-L" {
+ return Err(human(format!("Only `-l` and `-L` flags are allowed \
+ in {}: `{}`",
+ whence, value)))
+ }
+ let value = match flags_iter.next() {
+ Some(v) => v,
+ None => return Err(human(format!("Flag in rustc-flags has no value\
+ in {}: `{}`",
+ whence, value)))
+ };
+ match flag {
+ "-l" => library_links.push(value.to_string()),
+ "-L" => library_paths.push(Path::new(value)),
+
+ // was already checked above
+ _ => return Err(human("only -l and -L flags are allowed"))
+ };
+ }
+ Ok((library_paths, library_links))
+ }
}
-use std::collections::HashSet;
+use std::collections::{HashSet, HashMap};
use std::dynamic_lib::DynamicLibrary;
use std::io::{fs, BufferedReader, USER_RWX};
use std::io::fs::{File, PathExtensions};
use util::{mod, CargoResult, ProcessBuilder, CargoError, human, caused_human};
use util::{Require, Config, internal, ChainError, Fresh, profile, join_paths};
-use self::custom_build::CustomBuildCommandOutput;
use self::job::{Job, Work};
use self::job_queue as jq;
use self::job_queue::JobQueue;
pub use self::context::{PlatformPlugin, PlatformPluginAndTarget};
pub use self::context::{PlatformRequirement, PlatformTarget};
pub use self::layout::{Layout, LayoutProxy};
+pub use self::custom_build::BuildOutput;
mod context;
mod compilation;
pub fn compile_targets<'a>(env: &str, targets: &[&'a Target], pkg: &'a Package,
deps: &PackageSet, resolve: &'a Resolve,
sources: &'a SourceMap,
- config: &'a Config<'a>)
+ config: &'a Config<'a>,
+ lib_overrides: HashMap<String, BuildOutput>)
-> CargoResult<Compilation> {
if targets.is_empty() {
return Ok(Compilation::new(pkg))
});
let mut cx = try!(Context::new(env, resolve, sources, deps, config,
- host_layout, target_layout, pkg));
+ host_layout, target_layout, pkg,
+ lib_overrides));
let mut queue = JobQueue::new(cx.resolve, deps, cx.config);
// First ensure that the destination directory exists
match cx.resolve.features(pkg.get_package_id()) {
Some(features) => {
for feat in features.iter() {
- let feat = feat.as_slice().chars()
- .map(|c| c.to_uppercase())
- .map(|c| if c == '-' {'_'} else {c})
- .collect::<String>();
- p = p.env(format!("CARGO_FEATURE_{}", feat).as_slice(), Some("1"));
+ p = p.env(format!("CARGO_FEATURE_{}",
+ envify(feat.as_slice())).as_slice(),
+ Some("1"));
}
}
None => {}
// list of `-l` flags to pass to rustc coming from custom build scripts
let additional_library_links = match File::open(&command_output_file) {
Ok(f) => {
- let flags = try!(CustomBuildCommandOutput::parse(
+ let flags = try!(BuildOutput::parse(
BufferedReader::new(f), name.as_slice()));
additional_library_paths.extend(flags.library_paths.iter().map(|p| p.clone()));
// doesn't have a build command
};
- let flags = try!(CustomBuildCommandOutput::parse(
+ let flags = try!(BuildOutput::parse(
BufferedReader::new(flags), name.as_slice()));
additional_library_paths.extend(flags.library_paths.iter().map(|p| p.clone()));
}
}
}
}
+
+fn envify(s: &str) -> String {
+ s.chars()
+ .map(|c| c.to_uppercase())
+ .map(|c| if c == '-' {'_'} else {c})
+ .collect()
+}
pub use self::cargo_rustc::{KindTarget, KindHost, Context, LayoutProxy};
pub use self::cargo_rustc::{PlatformRequirement, PlatformTarget};
pub use self::cargo_rustc::{PlatformPlugin, PlatformPluginAndTarget};
+pub use self::cargo_rustc::{BuildOutput};
pub use self::cargo_run::run;
pub use self::cargo_new::{new, NewOptions};
pub use self::cargo_doc::{doc, DocOptions};
assert_that(p.cargo_process("build"),
execs().with_status(101)
.with_stderr(format!("\
-Only `-l` and `-L` flags are allowed in build script of `foo v0.5.0 ({})`:
-`-aaa -bbb
-`",
+Only `-l` and `-L` flags are allowed in build script of `foo v0.5.0 ({})`: \
+`-aaa -bbb`",
p.url())));
})
"));
})
-
test!(links_duplicates {
let p = project("foo")
.file("Cargo.toml", r#"
"));
})
+test!(overrides_and_links {
+ let (_, target) = ::cargo::ops::rustc_version().unwrap();
+
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.5.0"
+ authors = []
+ build = "build.rs"
+
+ [dependencies.a]
+ path = "a"
+ "#)
+ .file("src/lib.rs", "")
+ .file("build.rs", r#"
+ use std::os;
+ fn main() {
+ assert_eq!(os::getenv("DEP_FOO_FOO").unwrap().as_slice(), "bar");
+ assert_eq!(os::getenv("DEP_FOO_BAR").unwrap().as_slice(), "baz");
+ }
+ "#)
+ .file(".cargo/config", format!(r#"
+ [target.{}.foo]
+ rustc-flags = "-l foo -L bar"
+ foo = "bar"
+ bar = "baz"
+ "#, target).as_slice())
+ .file("a/Cargo.toml", r#"
+ [project]
+ name = "a"
+ version = "0.5.0"
+ authors = []
+ links = "foo"
+ build = "build.rs"
+ "#)
+ .file("a/src/lib.rs", "")
+ .file("a/build.rs", "not valid rust code");
+
+ assert_that(p.cargo_process("build").arg("-v"),
+ execs().with_status(0)
+ .with_stdout("\
+Compiling a v0.5.0 (file://[..])
+ Running `rustc [..] --crate-name a [..]`
+Compiling foo v0.5.0 (file://[..])
+ Running `rustc build.rs [..]`
+ Running `rustc [..] --crate-name foo [..]`
+"));
+})
+